//
//  UIButton+YHExtension.swift
//  Adorbee
//
//  Created by amovision on 2024/11/26.
//

import UIKit

// 用于持有闭包的辅助类
private class ClosureSleeve {
  let closure: (UIControl) -> Void
    
  init(_ closure: @escaping (UIControl) -> Void) {
    self.closure = closure
  }
    
  @objc func invoke(_ sender: UIControl) {
    closure(sender)
  }
}

// 关联对象的key
private enum AssociatedKeys {
  static var closureSleeves: UInt8 = 0
}

public extension UIControl {
  /// 为控件添加事件处理闭包
  /// - Parameters:
  ///   - events: 需要监听的事件
  ///   - closure: 事件触发时执行的闭包
  func addHandler(for events: UIControl.Event, closure: @escaping (UIControl) -> Void) {
    // 创建闭包持有者
    let sleeve = ClosureSleeve(closure)
        
    // 将闭包持有者存储为关联对象
    // 使用数组存储多个事件的闭包
    var sleeves = objc_getAssociatedObject(self, &AssociatedKeys.closureSleeves) as? [ClosureSleeve] ?? []
    sleeves.append(sleeve)
    objc_setAssociatedObject(self, &AssociatedKeys.closureSleeves, sleeves, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        
    // 添加目标动作
    addTarget(sleeve, action: #selector(ClosureSleeve.invoke(_:)), for: events)
  }
    
  /// 移除所有通过Block方式添加的事件处理
  func removeAllEventHandlers() {
    // 清除关联的闭包持有者
    objc_setAssociatedObject(self, &AssociatedKeys.closureSleeves, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        
    // 移除所有目标动作
    removeTarget(nil, action: nil, for: .allEvents)
  }
}

public typealias YHButtonHandler = (UIButton) -> Void
public extension UIButton {
  enum AttributeType { case title, image, bImage }
    
  convenience init(
    titles: Any? = nil,
    images: Any? = nil,
    bImages: Any? = nil,
    states: [UIControl.State]? = [.normal,.selected],
    handler: YHButtonHandler? = nil
  ) {
    self.init()
    set(titles: titles, images: images, bImages: bImages, states: states)
    addEventHandler(handler)
  }
    
  convenience init(title: Any? = nil, image: Any? = nil, bImage: Any? = nil, state: UIControl.State = .normal, handler: YHButtonHandler? = nil) {
    self.init()
    set(title: title, image: image, bImage: bImage, state: state)
    addEventHandler(handler)
  }
    
  func set(title: Any? = nil, image: Any? = nil, bImage: Any? = nil, state: UIControl.State = .normal) {
    if title != nil { set(attribute: title, state: state, type: .title) }
    if image != nil { set(attribute: image, state: state, type: .image) }
    if bImage != nil { set(attribute: bImage, state: state, type: .bImage) }
  }
    
  func clear(_ type: AttributeType, state: UIControl.State = .normal) {
    set(attribute: nil, state: state, type: type)
  }
    
  func set(titles: Any? = nil, images: Any? = nil, bImages: Any? = nil, states: [UIControl.State]?) {
    if titles != nil { set(attributes: titles, states: states, type: .title) }
    if images != nil { set(attributes: images, states: states, type: .image) }
    if bImages != nil { set(attributes: bImages, states: states, type: .bImage) }
  }
    
  private func set(attributes: Any?, states: [UIControl.State]?, type: AttributeType) {
    let sts = states ?? [.normal]
    sts.enumerated().forEach { set(attributes: attributes, idx: $0.0, state: $0.1, type: type) }
  }
    
  private func set(attributes: Any?, idx: Int, state: UIControl.State, type: AttributeType) {
    if let attrs = attributes as? [Any] {
      guard idx < attrs.count else { return }
      set(attribute: attrs[idx], state: state, type: type)
    } else {
      set(attribute: attributes, state: state, type: type)
    }
  }
    
  private func set(attribute: Any?, state: UIControl.State, type: AttributeType) {
    switch type {
    case .title:
      setAttributedTitle(attribute != nil ? AString(attribute, font: .num(16.6), color: .label) : nil, for: state)
    case .image:
      let image: UIImage? = if let img = attribute as? UIImage {
        img.withRenderingMode(.alwaysOriginal)
      } else if let name = attribute as? String {
        UIImage(named: name)?.withRenderingMode(.alwaysOriginal)
      } else { nil }
      setImage(image, for: state)
    case .bImage:
      let image: UIImage? = if let name = attribute as? String { UIImage(named: name) }
      else if let img = attribute as? UIImage { img }
      else if let color = attribute as? UIColor { UIImage(color: color, size: [10, 10].size) }
      else { nil }
      setBackgroundImage(image, for: state)
    }
  }
    
  func addAction(_ sel: Selector, target: Any?) {
    addTarget(target, action: sel, for: .touchUpInside)
  }
    
  func addEventHandler(_ handler: YHButtonHandler?) {
    addHandler(for: .touchUpInside) { control in
      handler?(control as! UIButton)
    }
  }
    
  static func gradient(_ title: Any, colors: [UIColor] = [Theme.light, Theme.dark], height: CGFloat = 44, handler: YHButtonHandler? = nil) -> UIButton {
    let btn = UIButton(title: AString(title, font: .num(16), color: .white), bImage: UIImage.gradient(colors: colors, size: [10].size), handler: handler)
    btn.addCorner(height/2)
    return btn
  }

  static func gradient(_ title: Any, colors: [UIColor] = [Theme.light, Theme.dark], height: CGFloat = 44, action: Selector, target: Any?) -> UIButton {
    let btn = UIButton(title: AString(title, font: .num(16), color: .white), bImage: UIImage.gradient(colors: colors, size: [10].size))
    btn.addAction(action, target: target)
    btn.addCorner(height/2)
    return btn
  }
    
  static func border(_ title: Any, color: UIColor = Theme.dark, hColor: UIColor = .white, height: CGFloat = 44, handler: YHButtonHandler? = nil) -> UIButton {
    let btn = UIButton(title: AString(title, font: .num(16), color: color), handler: handler)
    btn.set(title: AString(title, font: .num(16), color: hColor), bImage: color, state: .highlighted)
    btn.addCorner(height/2, borderWidth: 1, borderColor: color)
    return btn
  }
    
  static func border(_ title: Any, color: UIColor = Theme.dark, hColor: UIColor = .white, height: CGFloat = 44, action: Selector, target: Any?) -> UIButton {
    let btn = UIButton(title: AString(title, font: .num(16), color: color))
    btn.set(title: AString(title, font: .num(16), color: hColor), bImage: color, state: .highlighted)
    btn.addCorner(height/2, borderWidth: 1, borderColor: color)
    btn.addAction(action, target: target)
    return btn
  }
    
  static func title(_ title: Any, action: Selector, target: Any?) -> UIButton {
    let btn = UIButton(title: title)
    btn.addAction(action, target: target)
    return btn
  }
}
